/*
    cim.cpp
*/

#ifdef CTA_CS_QT
#include <QDir>
#endif

#include <cssysdef.h>
#include <time.h>
#include <string>
#include <iostream>
#include <stdio.h>

//CS includes
#include <cstool/initapp.h>
#include <csutil/sysfunc.h>
#include <csutil/event.h>
#include <iutil/eventq.h>
#include <iutil/event.h>
#include <iutil/csinput.h>
#include <iutil/vfs.h>
#include <iutil/object.h>
#include <iutil/objreg.h>
#include <iutil/plugin.h>
#include <ivaria/collider.h>
#include <imap/modelload.h>
// Sound
#include <isndsys/ss_source.h>
#include <isndsys/ss_loader.h>
#include <isndsys/ss_renderer.h>
#include <isndsys/ss_manager.h>
//CEL includes:
//#include "physicallayer/pl.h"
//#include "behaviourlayer/bl.h"
//#include "physicallayer/propclas.h"

#include "config.h"
#include "language.h"
#include "cs.h"
#include "catchthealien.h"

CS_IMPLEMENT_APPLICATION

//-----------------------------------------------------------------------------

catchthealien::catchthealien ()
{
  SetApplicationName ("CatchTheAlien");
}

catchthealien::~catchthealien ()
{
}

bool catchthealien::Setup ()
{
  // Now get the pointer to various modules we need. We fetch them
  // from the object registry. The RequestPlugins() call we did earlier
  // registered all loaded plugins with the object registry.
  // The virtual clock.

	cs::c().obj_reg = GetObjectRegistry();

	cs::c().g3d = csQueryRegistry<iGraphics3D> (GetObjectRegistry());
	if (!cs::c().g3d) return ReportError("Failed to locate 3D renderer!");

	cs::c().engine = csQueryRegistry<iEngine> (GetObjectRegistry());
	if (!cs::c().engine) return ReportError("Failed to locate 3D engine!");

	cs::c().vc = csQueryRegistry<iVirtualClock> (GetObjectRegistry());
	if (!cs::c().vc) return ReportError("Failed to locate Virtual Clock!");

	kbd = csQueryRegistry<iKeyboardDriver> (GetObjectRegistry());
	if (!kbd) return ReportError("Failed to locate Keyboard Driver!");

	joy = csQueryRegistry<iJoystickDriver> (GetObjectRegistry());
	if (joy==0)
	{
		return ReportError("crystalspace.application.joytest",
				"No iJoystickDriver plugin ?!");
	}

	cs::c().loader = csQueryRegistry<iLoader> (GetObjectRegistry());
	if (!cs::c().loader) return ReportError("Failed to locate Loader!");

	plugin_mgr = csQueryRegistry<iPluginManager>(GetObjectRegistry());

	model_ldr = csLoadPlugin<iModelLoader> (plugin_mgr, "crystalspace.mesh.loader.factory.sprite.3d.md2");

	cdsys = csQueryRegistry<iCollideSystem> (GetObjectRegistry());
	if (!cdsys) return ReportError ("Failed to locate CD system!");

	cs::c().fs = csQueryRegistry<iVFS> (GetObjectRegistry());
	cs::c().fs->Mount("/this/", "$.$/");

	reporter = csQueryRegistry<iReporter> (GetObjectRegistry());
	if (!reporter) return ReportError("Failed to locate reporter plugin!");

  // Sound Renderer
	cs::c().sndrenderer = csQueryRegistry<iSndSysRenderer> (GetObjectRegistry());
	if (!cs::c().sndrenderer) ReportError("Failed to locate Sound renderer!");

	csRef<iCommandLineParser> cmdline(CS_QUERY_REGISTRY (object_reg, iCommandLineParser));

	// Set Main Window Title
	if(cs::c().g3d->GetDriver2D()->GetNativeWindow())
		cs::c().g3d->GetDriver2D()->GetNativeWindow()->SetTitle("CatchTheAlien");

	// Load Fonts
	iFontServer *fs = cs::c().g3d->GetDriver2D()->GetFontServer ();
	int fontheight = cs::c().g3d->GetDriver2D()->GetHeight() / 34; // 34 lines of display is enough.
	cs::c().mainfont = fs->LoadFont ("/this/data/fonts/Purisa.ttf", fontheight);
	if(cs::c().mainfont == 0) cs::c().mainfont = fs->LoadFont (CSFONT_LARGE);
	// Load Fonts \


	LoadConfig();

	if (!cmdline)
		return ReportError("Failed to set up command line parser!");
	if (cmdline->GetOption("enable-python-console")) use_console=true;
	if (use_console)
	{
		console.AttachNew(new ctaConsole);
		if ( !console->Initialize(GetObjectRegistry()))
			return ReportError("Failed to initialize console!");
	  //console->Show();
	}
#ifdef DEBUG
	std::cout <<"2" << std::endl;
#endif
	InitCTA();
#ifdef DEBUG
	std::cout <<"3" << std::endl;
#endif
	return true;
}
void catchthealien::ProcessFrame ()
{
  // Tell 3D driver we're going to display 3D things.
  if (!cs::c().g3d->BeginDraw (cs::c().engine->GetBeginDrawFlags () | CSDRAW_3DGRAPHICS))
    return;

  RunCTA();
  if (getCta_state() == eExit)
  {
    // The user pressed escape to exit the application.
    // The proper way to quit a Crystal Space application
    // is by broadcasting a csevQuit event. That will cause the
    // main runloop to stop. To do that we get the event queue from
    // the object registry and then post the event.
    csRef<iEventQueue> q =
		  csQueryRegistry<iEventQueue> (GetObjectRegistry());
    if (q.IsValid()) q->GetEventOutlet()->Broadcast(csevQuit(GetObjectRegistry()));
  }
}

void catchthealien::FinishFrame ()
{

	cs::c().g3d->FinishDraw ();
	cs::c().g3d->Print (0);

}

bool catchthealien::OnKeyboard(iEvent& ev)
{

  return false;
}

bool catchthealien::OnInitialize(int /*argc*/, char* /*argv*/ [])
{
  // RequestPlugins() will load all plugins we specify. In addition
  // it will also check if there are plugins that need to be loaded
  // from the config system (both the application config and CS or
  // global configs). In addition it also supports specifying plugins
  // on the commandline.

	  // Load application-specific configuration file.
#ifdef CTA_CS_QT
	if (!csInitializer::SetupConfigManager(GetObjectRegistry (),
              QDir::tempPath().append("/cta.cfg").toAscii()))
            return ReportError("Failed to initialize configuration manager!");
#else
	if (!csInitializer::SetupConfigManager(GetObjectRegistry (),
	      "/this/data/config/cta.cfg"))
	    return ReportError("Failed to initialize configuration manager!");
#endif

  iObjectRegistry *object_reg;
  if (!csInitializer::RequestPlugins(object_reg = GetObjectRegistry(),
    CS_REQUEST_VFS,
    CS_REQUEST_OPENGL3D,
    CS_REQUEST_ENGINE,
    CS_REQUEST_FONTSERVER,
    CS_REQUEST_IMAGELOADER,
    CS_REQUEST_LEVELLOADER,
    CS_REQUEST_REPORTER,
    CS_REQUEST_REPORTERLISTENER,
    CS_REQUEST_PLUGIN("crystalspace.collisiondetection.opcode",
		iCollideSystem),
	CS_REQUEST_PLUGIN("crystalspace.console.output.standard",
		iConsoleOutput),
	CS_REQUEST_PLUGIN ("crystalspace.device.joystick",
		iEventPlug),
	CS_REQUEST_PLUGIN("crystalspace.font.server.freetype2",
		iFontServer),
//	CS_REQUEST_PLUGIN("crystalspace.mesh.loader.factory.genmesh.md2",
//		iModelLoader),
		// TODO: Sound not working
	//CS_REQUEST_PLUGIN("crystalspace.sndsys.renderer.software",
	//	iSndSysRenderer),
	//CS_REQUEST_PLUGIN("cel.physicallayer",
	//	iCelPlLayer),
	//CS_REQUEST_PLUGIN("cel.behaviourlayer.test:iCelBlLayer.Test",
	//	iCelBlLayer),
//	CS_REQUEST_PLUGIN("cel.persistance.classic",
//		iCelPersistance),

    CS_REQUEST_END))
    return ReportError("Failed to initialize plugins!");

  LoadConfig();
  if(enable_logging)
    {
  	  listener = CS_QUERY_REGISTRY(GetObjectRegistry(),iStandardReporterListener);
  	  listener->SetMessageDestination(CS_REPORTER_SEVERITY_ERROR,true,false,true,false,true,false);
  	  listener->SetMessageDestination(CS_REPORTER_SEVERITY_WARNING,true,false,true,false,true,false);
  	  listener->SetMessageDestination(CS_REPORTER_SEVERITY_NOTIFY,true,false,true,false,true,false);
  	  listener->SetMessageDestination(CS_REPORTER_SEVERITY_DEBUG,true,false,true,false,true,false);
  	  listener->SetDebugFile(debug_filename,append_logfile);

  	  char dateStr [9];
  	  char timeStr [9];
  	  //_strdate( dateStr);
  	  //_strtime( timeStr );
  	  csString logStr("---------------------------------------");
  	  logStr+="\n";
  	  logStr+="CTA started at ";
  	  logStr+=dateStr;
  	  logStr+=" : ";
  	  logStr+=timeStr;

  	  csReporterHelper::Report(GetObjectRegistry(),CS_REPORTER_SEVERITY_NOTIFY,"cta",logStr.GetData());
    }

  csBaseEventHandler::Initialize(GetObjectRegistry());

  // Now we need to setup an event handler for our application.
  // Crystal Space is fully event-driven. Everything (except for this
  // initialization) happens in an event.
  if (!RegisterQueue(GetObjectRegistry(), csevAllEvents(GetObjectRegistry())))
    return ReportError("Failed to set up event handler!");

  return true;
}

void catchthealien::OnExit()
{
}

bool catchthealien::Application()
{
	// Open the main system. This will open all the previously loaded plug-ins.
	// i.e. all windows will be opened.
	if (!OpenApplication (GetObjectRegistry()))
		return ReportError ("Error opening system!");


  	// --pl = CS_QUERY_REGISTRY (object_reg, iCelPlLayer);
  	// --bltest = CS_QUERY_REGISTRY_TAG_INTERFACE(object_reg, "iCelBlLayer.Test", iCelBlLayer);
  	// Setup
  	if (!Setup())
  		return false;

  // This calls the default runloop. This will basically just keep
  // broadcasting process events to keep the game going.
  	Run ();

  	return true;
}

bool catchthealien::LoadConfig()
{
	csRef<iConfigManager> confman (CS_QUERY_REGISTRY (GetObjectRegistry(), iConfigManager));
	startmap = confman->GetStr("Cta.Settings.StartLevel","terrain");
	use_console = confman->GetBool("Cta.Settings.EnableConsole", false);
	enable_logging = confman->GetBool("Cta.Settings.EnableLogging",true);
	debug_filename = confman->GetStr("Cta.Settings.DebugFileName","/debug.log");
	append_logfile = confman->GetBool("Cta.Settings.AppendLogFile",true);

	return true;
}

void catchthealien::setGameType(char* type)
{
    game_type = type;
}

bool catchthealien::InitCTA()
{
#ifdef CTA_CS_QT
    cta_state_ = eGameInit;
#else
	cta_state_ = eInit;
	game_type = "single";
#endif

	return true;
}

bool catchthealien::RunCTA()
{
#ifdef DEBUG
	//std::cout << "cta_state: " << cta_state_ << std::endl;
#endif
	switch (cta_state_)
	{
		case eInit:
		{
		// Read Language File
		cta::language::l().Init();
#ifdef DEBUG
		std::cout << "Starting ... " << cta::language::l().Get(2,"") << std::endl;
#endif

		// Read COnfig File
			cta::config::c().ReadConfig();

		// Initialize Framelimiter
			framelimiter_.Init(1);

		// Initialize Intro
			if(intro_.Init()) cta_state_ = eIntro;
		} break;
		case eIntro:
		{
			if(intro_.getState() == true)
			{
				cta_state_ = eMenuInit;
				break;
			}

			intro_.Run();
		} break;
		case eMenuInit:
		{
			menu_.Init();
				//TODO: ESC Key triggers Exit
			cta_state_ = eMenu;
		} break;
		case eMenu:
		{
			// Menu RUN
			if(menu_.getState() == 0)
			{
					//Game Speed Limiter
				if(!framelimiter_.elapsed())
					csSleep(1);
				//TODO: Sleep()
				menu_.Run();
			// Menu EXIT
			} else if(menu_.getState()==(-1)){
				cta_state_ = eExit;
			// MENU START SINGLE
			} else if(menu_.getState()==1){
				cta_state_ = eGameInit;
				game_type = "single";
			// MENU START MULTI
			} else if(menu_.getState()==2){
				cta_state_ = eGameInit;
				game_type = "multi";
			// Menu EXIT
			}else {
				cta_state_ = eExit;
			}
		} break;
		case eGameInit:
		{
			if(game_.Init(game_type))
				cta_state_ = eGame;
		}break;
		case eGame:
		{
			if(!game_.getState())
			{
					// Game Speed Limiter
				if(!framelimiter_.elapsed())
					csSleep(1);
					// Game Routines
				game_.Run();
			}else{
#ifdef CTA_CS_QT
				cta_state_ = eExit;
#else
				cta_state_ = eMenuInit;
#endif
			}
			//cta_state_ = eExit;
		}break;
		case eExit:
		{
			// Write Config to /~/.cta/config.xml
			cta::config::c().WriteConfig();
				// release Graphics
				//graphics::Destroy();
		} break;
	}
	return true;
}
